package rpc.packet.codec;

import rpc.common.enumeraion.CompressType;
import rpc.common.enumeraion.PackageType;
import rpc.common.enumeraion.RpcError;
import rpc.common.enumeraion.SerializerCode;
import rpc.common.exception.RpcException;
import rpc.common.extension.ExtensionLoader;
import rpc.intf.CommonSerializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import lombok.extern.slf4j.Slf4j;
import rpc.intf.Compress;
import rpc.intf.impl.compressAlgorithm.GzipCompress;
import rpc.intf.impl.serializer.KryoSerializer;
import rpc.packet.RpcMessage;
import rpc.packet.RpcRequest;
import rpc.packet.RpcResponse;


import java.util.List;

@Slf4j
public class CommonCodec extends ByteToMessageCodec<RpcMessage> {

    //magic 4 bytes
    private static final int MAGIC_NUMBER = 0xCAFEBABE;

    public CommonCodec(){}

    /**
     * 编码器
     * +---------------+---------------+-----------------+--------------+--------------+
     * |  Magic Number |  Package Type | Serializer Type | Compress Type| Data Length |
     * |    4 bytes    |    1 byte    |     1 byte       |   1 byte     |    4 bytes  |
     * +---------------+---------------+-----------------+-------------+--------------+
     * |                          Data Bytes                                          |
     * |                   Length: ${Data Length}                                     |
     * +------------------------------------------------------------------------------+
     */
    /**
     * 编码
     * @param channelHandlerContext
     * @param rpcMessage
     * @param out
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, RpcMessage rpcMessage, ByteBuf out) throws Exception {
        out.writeInt(MAGIC_NUMBER);

        out.writeByte(rpcMessage.getPackageType());
        out.writeByte(rpcMessage.getSerializer());
        out.writeByte(rpcMessage.getCompress());

        //利用加载器 加载一个 单列
        //CommonSerializer commonSerializer = new KryoSerializer();
        CommonSerializer commonSerializer = ExtensionLoader
                .getExtensionLoader(CommonSerializer.class)
                .getExtension(SerializerCode.getName(rpcMessage.getSerializer()));

        byte[] bodyBytes = commonSerializer.serialize(rpcMessage.getData());

        //利用加载器 加载一个 单列
        //Compress compress = new GzipCompress();
        Compress compress = ExtensionLoader
                .getExtensionLoader(Compress.class)
                .getExtension(CompressType.getName(rpcMessage.getCompress()));

        bodyBytes = compress.compress(bodyBytes);
        out.writeInt(bodyBytes.length);
        out.writeBytes(bodyBytes);
    }

    /**
     * 解码
     * @param channelHandlerContext
     * @param in
     * @param out
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List out) throws Exception {
        int magic = in.readInt();
        if (magic != MAGIC_NUMBER) {
            log.debug("不识别的协议包: {}", magic);
            throw new RpcException(RpcError.UNKNOWN_PROTOCOL);
        }
        //获取包类型
        byte packageCode = in.readByte();
        //获取对应的序列化器
        byte serializerCode = in.readByte();
        //获取对应的压缩器
        byte compressCode = in.readByte();
        RpcMessage rpcMessage = RpcMessage.builder()
                .packageType(packageCode)
                .serializer(serializerCode)
                .compress(compressCode).build();

        Class<?> packageClass = PackageType.getPackageType(packageCode);

        if (packageClass == null) {
            log.debug("不识别的数据包: {}", packageCode);
            throw new RpcException(RpcError.UNKNOWN_PACKET_TYPE);
        }

        log.debug("解码的对象类型: {}", packageClass);

        int length = in.readInt();
        byte[] bodyBytes = new byte[length];
        in.readBytes(bodyBytes);
        //利用加载器 加载一个 单列
        Compress compress = ExtensionLoader
                .getExtensionLoader(Compress.class)
                .getExtension(CompressType.getName(compressCode));
        bodyBytes = compress.decompress(bodyBytes);

        //利用加载器 加载一个 单列
        CommonSerializer commonSerializer = ExtensionLoader
                .getExtensionLoader(CommonSerializer.class)
                .getExtension(SerializerCode.getName(serializerCode));

        Object obj = commonSerializer.deserialize(bodyBytes, packageClass);

        rpcMessage.setData(obj);

        out.add(rpcMessage);
    }
}
